/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.executor.keygen;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;

/**
 * @author Clinton Begin
 */
/**
 * JDBC3键值生成器,核心是使用JDBC3的Statement.getGeneratedKeys
 * 例如<insert id="insert" useGeneratedKeys="true" keyProperty="id">
 * 
 */
public class Jdbc3KeyGenerator implements KeyGenerator {
	// jdbc模式只能是执行之后返回主键id
	@Override
	public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
		// do nothing
	}

	@Override
	public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
		List<Object> parameters = new ArrayList<Object>();
		parameters.add(parameter);
		processBatch(ms, stmt, parameters);
	}

	/** 
	 * 批处理过程
	 * @param ms
	 * @param stmt
	 * @param parameters
	 */
	public void processBatch(MappedStatement ms, Statement stmt, List<Object> parameters) {
		ResultSet rs = null;
		try {
			// 核心是使用JDBC3的Statement.getGeneratedKeys
			rs = stmt.getGeneratedKeys();
			final Configuration configuration = ms.getConfiguration();
			final TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
			// 获得配置的属性字段名
			// 获得返回值的参数名
			final String[] keyProperties = ms.getKeyProperties();
			// 返回的id值
			final ResultSetMetaData rsmd = rs.getMetaData();
			TypeHandler<?>[] typeHandlers = null;
			if (keyProperties != null && rsmd.getColumnCount() >= keyProperties.length) {
				for (Object parameter : parameters) {// 循环用于批量insert操作
					// there should be one row for each statement (also one for
					// each parameter)
					if (!rs.next()) {
						break;
					}
					// 将javabean 包装
					final MetaObject metaParam = configuration.newMetaObject(parameter);
					// 类型转换，一般为java基础类型转换
					if (typeHandlers == null) {
						// 先取得类型处理器
						typeHandlers = getTypeHandlers(typeHandlerRegistry, metaParam, keyProperties);
					}
					// 填充键值
					populateKeys(rs, metaParam, keyProperties, typeHandlers);
				}
			}
		} catch (Exception e) {
			throw new ExecutorException(
					"Error getting generated key or setting result to parameter object. Cause: " + e, e);
		} finally {
			if (rs != null) {
				try {
					rs.close();
				} catch (Exception e) {
					// ignore
				}
			}
		}
	}

	/**
	 * 得到类型处理器
	 * 
	 * @param typeHandlerRegistry
	 * @param metaParam
	 * @param keyProperties
	 * @return
	 */
	// 设置执行sql之后返回参数对应的值
	private TypeHandler<?>[] getTypeHandlers(TypeHandlerRegistry typeHandlerRegistry, MetaObject metaParam,
			String[] keyProperties) {
		TypeHandler<?>[] typeHandlers = new TypeHandler<?>[keyProperties.length];
		for (int i = 0; i < keyProperties.length; i++) {
			if (metaParam.hasSetter(keyProperties[i])) {
				Class<?> keyPropertyType = metaParam.getSetterType(keyProperties[i]);
				TypeHandler<?> th = typeHandlerRegistry.getTypeHandler(keyPropertyType);
				typeHandlers[i] = th;
			}
		}
		return typeHandlers;
	}

	/**
	 * 填充键值
	 * 
	 * @param rs
	 * @param metaParam
	 * @param keyProperties
	 * @param typeHandlers
	 * @throws SQLException
	 */
	private void populateKeys(ResultSet rs, MetaObject metaParam, String[] keyProperties, TypeHandler<?>[] typeHandlers)
			throws SQLException {
		for (int i = 0; i < keyProperties.length; i++) {
			TypeHandler<?> th = typeHandlers[i];
			if (th != null) {
				// 获得执行后对应参数的值
				// 获得db->java的值
				Object value = th.getResult(rs, i + 1);
				// 设值到javabean对应的字段属性中
				metaParam.setValue(keyProperties[i], value);
			}
		}
	}

}
